package com.gzc.just.play.last.war.sceneserver.damage;

import com.gzc.just.play.last.war.sceneserver.gameobject.base.GameObjectCamp;
import com.gzc.just.play.last.war.sceneserver.gameobject.base.GameObjectType;
import com.gzc.just.play.last.war.sceneserver.gameobject.unit.GameUnit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;

import static org.junit.jupiter.api.Assertions.*;

/**
 * DamageCalculator 单元测试 - 测试 15 个测试用例
 * 覆盖伤害计算、防御、暴击、躲避、治疗等功能
 */
@DisplayName("伤害计算器单元测试")
class DamageCalculatorTest {

    private GameUnit attacker;
    private GameUnit defender;

    /**
     * 测试用的 GameUnit 实现类
     */
    static class TestGameUnit extends GameUnit {
        public TestGameUnit(long objectId, GameObjectType objectType, GameObjectCamp camp,
                           int maxHealth, int maxMana, double attack, double defense, double speed) {
            super(objectId, objectType, camp, maxHealth, maxMana, attack, defense, speed);
        }

        @Override
        protected void checkDeadState() {
        }
    }

    @BeforeEach
    void setUp() {
        attacker = new TestGameUnit(1, GameObjectType.HERO, GameObjectCamp.TEAM_1, 1000, 500, 100, 50, 100);
        defender = new TestGameUnit(2, GameObjectType.HERO, GameObjectCamp.TEAM_2, 1000, 500, 80, 60, 80);
        attacker.initialize();
        defender.initialize();
    }

    // ========== 基础伤害计算 ==========

    @Test
    @DisplayName("测试1: 基础伤害计算")
    void testBaseDamageCalculation() {
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        assertNotNull(result);
        // 基础伤害应该被记录
        assertTrue(result.getBaseDamage() >= 0);
        // 最终伤害应该为正数或者躲避了
        assertTrue(result.getFinalDamage() > 0 || result.isDodged());
    }

    @Test
    @DisplayName("测试2: 零伤害处理")
    void testZeroDamage() {
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 0, DamageType.PHYSICAL);
        assertEquals(0, result.getFinalDamage());
    }

    @Test
    @DisplayName("测试3: 负伤害处理")
    void testNegativeDamage() {
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, -100, DamageType.PHYSICAL);
        assertEquals(0, result.getFinalDamage());
    }

    // ========== 防御力计算 ==========

    @Test
    @DisplayName("测试4: 防御力减伤")
    void testDefenseReduction() {
        defender.setSpeed(0); // 设置速度为0，确保不会触发躲避
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 200, DamageType.PHYSICAL);
        assertTrue(result.getFinalDamage() < 200);
    }

    @Test
    @DisplayName("测试5: 防御力不能完全抵消伤害")
    void testDefenseMinimumDamage() {
        defender.setDefense(500);
        defender.setSpeed(0); // 设置速度为0，确保不会触发躲避
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        // 最小伤害为 1
        assertTrue(result.getFinalDamage() >= 1);
    }

    @Test
    @DisplayName("测试6: 伤害类型影响防御")
    void testDamageTypeDefense() {
        defender.setSpeed(0); // 设置速度为0，确保不会触发躲避
        DamageResult physicalResult = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        DamageResult pureDamageResult = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PURE);
        
        // 纯伤害和物理伤害都计算，但结果取决于防御计算
        assertTrue(physicalResult.getFinalDamage() > 0);
        assertTrue(pureDamageResult.getFinalDamage() > 0);
    }

    // ========== 暴击与躲避 ==========

    @Test
    @DisplayName("测试7: 暴击时伤害增加")
    void testCriticalStrike() {
        attacker.setCriticalRate(1.0);
        // 暴击率设为100%，但可能被躲避，所以不能绝对保证暴击
        // 进行多次测试找到至少一次暴击
        boolean found = false;
        for (int i = 0; i < 50; i++) {
            DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
            if (result.isCritical()) {
                found = true;
                break;
            }
        }
        assertTrue(found);
    }

    @Test
    @DisplayName("测试8: 躲避减少伤害")
    void testDodge() {
        defender.setSpeed(2000);
        boolean dodged = false;
        for (int i = 0; i < 100; i++) {
            DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
            if (result.isDodged()) {
                dodged = true;
                break;
            }
        }
        assertTrue(dodged);
    }

    @Test
    @DisplayName("测试9: 低速单位躲避率低")
    void testLowSpeedLowDodge() {
        defender.setSpeed(10);
        int dodgeCount = 0;
        for (int i = 0; i < 100; i++) {
            DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
            if (result.isDodged()) {
                dodgeCount++;
            }
        }
        assertTrue(dodgeCount < 50);
    }

    // ========== 技能伤害 ==========

    @Test
    @DisplayName("测试10: 技能伤害计算")
    void testSkillDamage() {
        defender.setSpeed(0); // 设置速度为0，确保不会触发躲避
        DamageResult result = DamageCalculator.calculateSkillDamage(attacker, defender, 101, 1.5f);
        assertNotNull(result);
        assertTrue(result.getFinalDamage() > 0);
    }

    @Test
    @DisplayName("测试11: 普通攻击伤害")
    void testNormalAttackDamage() {
        DamageResult result = DamageCalculator.calculateNormalAttackDamage(attacker, defender);
        assertNotNull(result);
        // 普通攻击伤害应该至少为1（最小伤害值）
        assertTrue(result.getFinalDamage() >= 1 || result.isDodged());
    }

    // ========== 特殊伤害类型 ==========

    @Test
    @DisplayName("测试12: 真实伤害")
    void testTrueDamage() {
        DamageResult result = DamageCalculator.calculateTrueDamage(attacker, defender, 100);
        assertEquals(100, result.getFinalDamage());
        assertEquals(DamageType.TRUE_DAMAGE, result.getDamageType());
    }

    @Test
    @DisplayName("测试13: 治疗效果")
    void testHealing() {
        DamageResult result = DamageCalculator.calculateHealing(attacker, defender, 200);
        assertEquals(-200, result.getBaseDamage());
        assertTrue(result.getFinalDamage() <= 0);
    }

    // ========== 伤害结果验证 ==========

    @Test
    @DisplayName("测试14: 伤害结果包含正确的单位ID")
    void testDamageResultUnitIds() {
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        assertTrue(result.getAttackerId() > 0);
        assertTrue(result.getDefenderId() > 0);
    }

    @Test
    @DisplayName("测试15: 伤害结果包含时间戳")
    void testDamageResultTimestamp() {
        long beforeTime = System.currentTimeMillis();
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        long afterTime = System.currentTimeMillis();
        
        assertTrue(result.getTimestamp() >= beforeTime);
        assertTrue(result.getTimestamp() <= afterTime);
    }

    // ========== 高级测试 ==========

    @Test
    @DisplayName("测试扩展1: 多次伤害计算")
    void testMultipleDamageCalculations() {
        for (int i = 0; i < 10; i++) {
            DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
            assertNotNull(result);
        }
    }

    @Test
    @DisplayName("测试扩展2: 伤害加成")
    void testDamageBonus() {
        attacker.setAttack(200);
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        assertTrue(result.getDamageAfterBonus() > 0 || result.getDamageAfterBonus() == 0);
    }

    @Test
    @DisplayName("测试扩展3: 不同伤害类型的处理")
    void testDifferentDamageTypes() {
        DamageResult physical = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        DamageResult magical = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.MAGICAL);
        DamageResult pure = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PURE);
        
        assertNotNull(physical);
        assertNotNull(magical);
        assertNotNull(pure);
    }

    @Test
    @DisplayName("测试扩展4: 技能伤害倍率")
    void testSkillDamageRate() {
        DamageResult skillResult = DamageCalculator.calculateSkillDamage(attacker, defender, 101, 2.0f);
        assertNotNull(skillResult);
    }

    @Test
    @DisplayName("测试扩展5: 暴击倍数")
    void testCriticalMultiplier() {
        attacker.setCriticalRate(1.0);
        DamageResult result = DamageCalculator.calculateDamage(attacker, defender, 100, DamageType.PHYSICAL);
        if (result.isCritical()) {
            assertTrue(result.getCriticalMultiplier() > 1.0f);
        }
    }
}
